(*
Actor movement with two cameras (first-person and third-person)

   The movement control is a little "doom-like" and keyboard only.
   This demos mainly answers to "doom-like" movement questions and keyboard
   handling in GLScene.
   The basic principle is to check which key are pressed, and for each movement
   key, multiply the movement by the deltaTime and use this value as delta
   position or angle.
   The frame rate may not be that good on non-T&L accelerated board, mainly due
   to the mushrooms that are light on fillrate needs, but heavy on the polygons.
   This demonstrates how badly viewport object-level clipping is needed in
   GLScene :), a fair share of rendering power is lost in projecting
   objects that are out of the viewing frustum.

   TODO : 3rd person view with quaternion interpolation (smoother mvt)
          More mvt options (duck, jump...)
          Smooth animation transition for TGLActor
          HUD in 1st person view

   Carlos Arteaga Rivero <carteaga@superele.gov.bo>
*)
unit fGlsTreedemo;

interface

uses
  Winapi.Windows,
  System.SysUtils,
  System.Classes,
  Vcl.StdCtrls,
  Vcl.Buttons,
  Vcl.Controls,
  Vcl.ExtCtrls,
  Vcl.ComCtrls,
  Vcl.Forms,
  Vcl.Graphics,
  GLS.FileTGA,
  Vcl.Dialogs,
  Vcl.Imaging.Jpeg,
  Vcl.ExtDlgs,

  GLS.Cadencer,
  GLS.VectorFileObjects,
  GLS.VectorTypes,
  GLS.Scene,
  GLS.Objects,
  GLS.Material,
  GLS.GeomObjects,
  GLS.SkyDome,
  GLS.SceneViewer,
  GLS.Navigator,
  GLS.Texture,
  GLS.HUDObjects,
  GLS.Coordinates,
  GLS.VectorGeometry,
  GLS.Keyboard,
  GLS.BaseClasses,

  uConstants,
  uGlsVector,
  uGlsTree,
  uModelParameters;

type
  TATreeForm = class(TForm)
    GLScene1: TGLScene;
    GLCamera1: TGLCamera;
    DummyCube1: TGLDummyCube;
    Disk1: TGLDisk;
    GLSceneViewer1: TGLSceneViewer;
    Actor1: TGLActor;
    Actor2: TGLActor;
    GLCadencer1: TGLCadencer;
    Panel1: TPanel;
    Timer1: TTimer;
    GLCamera2: TGLCamera;
    Label3: TLabel;
    Label4: TLabel;
    DummyCube2: TGLDummyCube;
    FreeForm: TGLFreeForm;
    GLLightSource2: TGLLightSource;
    DummyCube3: TGLDummyCube;
    Label1: TLabel;
    SkyDome1: TGLSkyDome;
    GLNavigator1: TGLNavigator;
    GLUserInterface1: TGLUserInterface;
    CBMouseLook: TCheckBox;
    Panel2: TPanel;
    SpriteBtn: TSpeedButton;
    ObjectBtn: TSpeedButton;
    ClearBtn: TSpeedButton;
    CloseBtn: TSpeedButton;
    Tree3dLabel: TLabel;
    TreeSpriteLabel: TLabel;
    GLSprite1: TGLSprite;
    HelpBtn: TSpeedButton;
    TreeImageBtn: TSpeedButton;
    OpenPictureDialog1: TOpenPictureDialog;
    TrealLabel: TLabel;
    TrealTreeBtn: TSpeedButton;
    TreeFreeForm: TGLFreeForm;
    OpenTdfDialog: TOpenDialog;
    ProgressBar: TProgressBar;
    CancelButton: TButton;
    GLMaterialLibrary1: TGLMaterialLibrary;
    TreeLeafFF: TGLFreeForm;
    LeafOffCB: TCheckBox;
    GLDummyCube1: TGLDummyCube;
    FruitFF: TGLFreeForm;

    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure HandleKeys(const deltaTime: Double);
    procedure GLCadencer1Progress(Sender: TObject; const deltaTime,
      newTime: Double);
    procedure CBMouseLookClick(Sender: TObject);
    procedure CloseBtnClick(Sender: TObject);
    procedure ClearBtnClick(Sender: TObject);
    procedure ObjectBtnClick(Sender: TObject);
    procedure SpriteBtnClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormShow(Sender: TObject);
    procedure HelpBtnClick(Sender: TObject);
    procedure TreeImageBtnClick(Sender: TObject);
    procedure TrealTreeBtnClick(Sender: TObject);
    procedure CancelButtonClick(Sender: TObject);
  private
    FTree: TTree;
    TotalFruit, TotalFruitVertices,
    TotalDemoLeaves,TotalLeaveVertices: integer;
    TotalStems: integer;
    TotalVertices: integer;
    TotalFaces: integer;
    procedure AddMushrooms(NbMushrooms:Integer);
    procedure AddSprites(Spriter:Integer);
    procedure TrealTreeMaker;
    function Initialize(MaxX, MaxY, MaxZ: real; ATotalStems: integer): integer;
    function AddStem(Sections: TList): integer;
    function AddLeaf(Points: TList; LeafColor: integer; LeafColorVariation: boolean): integer;
    function AddFruit(Points: TList; FruitColor: integer; FruitColorVariation: boolean): integer;

  public
    //    TreeFormLoaded:Boolean;
    TreeCount, MushTreeCount, SpriteTreeCount:Integer;
  end;

var
  ATreeForm: TATreeForm;
  FModelParameters: TModelParameters;


implementation

{$R *.DFM}

uses
  uGlsTreeUnit;

const
  cWalkStep = 6;   // this is our walking speed, in 3D units / second
  cStrafeStep = 6; // this is our strafing speed, in 3D units / second
  cRotAngle = 60;  // this is our turning speed, in degrees / second
  cRunBoost = 2;   // speed boost when running
  cSpread = 90;    //range of random tree dispersal


procedure TATreeForm.FormCreate(Sender: TObject);
var 	picName : String;
begin
  Timer1.Enabled := False;
  GLCadencer1.Enabled := False;
  DemoTreePlanted:=False;
  GlsDemoCancelled:=False;
  MushTreeCount:=0;
  SpriteTreeCount:=0;
  TreeCount:=0;
  {TreeSprite    TreeBase    TreeTop
   TreeBark   TreeLeaf   TreeFruit}
   picName:=Extractfilepath(ParamStr(0))+'tree data'+'\ztree1.tga';
  GLMaterialLibrary1.Materials[0].Material.Texture.Image.LoadFromFile(picName);
  (* GLSprite1.Material.Texture.Image.LoadFromFile(picName);
  GLSprite1.Material.Texture.Disabled:=False;
  GLSprite1.Material.blendingmode := bmTransparency;
  GLSprite1.Material.Texture.texturemode   := tmReplace;{tmModulate;}
  GLSprite1.Material.Texture.imageAlpha    := tiaSuperBlackTransparent;
  GLSprite1.NoZWrite:=True;
  GLSprite1.ObjectsSorting := osRenderBlendedLast;    *)
   // Load mushroom mesh     and 2 images...
  picName:=Extractfilepath(ParamStr(0))+'tree data'+'\ztreetop_013.jpg';
  GLMaterialLibrary1.Materials[1].Material.Texture.Image.LoadFromFile(picName);
  FreeForm.Material.MaterialLibrary:=GLMaterialLibrary1;
  FreeForm.Material.LibMaterialName:='leaf';
  picName:=Extractfilepath(ParamStr(0))+'tree data'+'\zbark_016.jpg';
  GLMaterialLibrary1.Materials[2].Material.Texture.Image.LoadFromFile(picName);
  FreeForm.LoadFromFile(Extractfilepath(ParamStr(0))+'tree data'+'\zmushtree.3ds');


   // Load Actor into GLScene                  {}
   Actor1.LoadFromFile('tree quake\waste.md2');           {}
   Actor1.Material.Texture.Image.LoadFromFile('tree quake\waste.jpg');
   Actor1.Animations.LoadFromFile('tree quake\Quake2Animations.aaf');
   Actor1.Scale.SetVector(0.04, 0.04, 0.04, 0);
   // Load weapon model and texture        {}
   Actor2.LoadFromFile('tree quake\WeaponWaste.md2');
                                                          {}
   Actor2.Material.Texture.Image.LoadFromFile('tree quake\WeaponWaste.jpg');
   Actor2.Animations.Assign(Actor1.Animations);

   // Define animation properties
   Actor1.AnimationMode:=aamLoop;
   Actor1.SwitchToAnimation('stand');
   Actor1.FrameInterpolation:=afpLinear;
   Actor2.Synchronize(Actor1);

   // Load Texture for ground disk
   Disk1.Material.Texture.Image.LoadFromFile(Extractfilepath(ParamStr(0))+'tree data'+'\zclover.jpg');
   // Duplicate our reference mushroom (but not its mesh data !)
   AddMushrooms(1);
   AddSprites(1);
end;

procedure TATreeForm.TreeImageBtnClick(Sender: TObject);
begin
  OpenPictureDialog1.InitialDir:=Extractfilepath(ParamStr(0));
  If OpenPictureDialog1.Execute then
  begin
  GLMaterialLibrary1.Materials[0].Material.Texture.Image.LoadFromFile(OpenPictureDialog1.Filename);
 (*
{	GLMaterialLibrary1.Materials[0].}
  GLSprite1.Material.Texture.Image.LoadFromFile(OpenPictureDialog1.Filename);
  GLSprite1.Material.blendingmode := bmTransparency;
  GLSprite1.Material.Texture.texturemode   := tmReplace;{tmModulate;}
  GLSprite1.Material.Texture.imageAlpha    := tiaSuperBlackTransparent;
  GLSprite1.NoZWrite:=True;
  GLSprite1.ObjectsSorting := osRenderBlendedLast; *)
  end;
end;

procedure TATreeForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Timer1.Enabled := False;
  GLCadencer1.Enabled := False;
  GlsDemoCancelled:=True;
  If DemoTreePlanted then
  begin
  FTree.Destroy;{Tree data}
  {Freeform data... form contains it...}
  end;
end;
procedure TATreeForm.CancelButtonClick(Sender: TObject);
begin
  if MessageDlg('Are you sure?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
    GlsDemoCancelled := True;
end;
procedure TATreeForm.FormShow(Sender: TObject);
begin
  Timer1.Enabled := True;
  GLCadencer1.Enabled := True;
  GlsDemoCancelled:=False;
end;

procedure TATreeForm.HelpBtnClick(Sender: TObject);
begin
  Application.HelpContext(9900);
end;

procedure TATreeForm.CBMouseLookClick(Sender: TObject);
begin
   GLUserInterface1.MouseLookActive:=CBMouseLook.Checked;
end;

procedure TATreeForm.HandleKeys(const deltaTime: Double);
var
   moving : String;
   boost : Single;
begin
   // This function uses asynchronous keyboard check (see Keyboard.pas)
   if IsKeyDown(VK_ESCAPE) then Close;
   if IsKeyDown('A') then begin
      CBMouseLook.Checked:=True;
      CBMouseLookClick(Self);
   end;
   if IsKeyDown('D') then begin
      CBMouseLook.Checked:=False;
      CBMouseLookClick(Self);
   end;

   //Change Cameras
   if IsKeyDown(VK_F7) then begin
      GLSceneViewer1.Camera:=GLCamera1;
{}      Actor1.Visible:=True;
      Label4.Font.Style:=Label4.Font.Style-[fsBold];
      Label3.Font.Style:=Label3.Font.Style+[fsBold];
   end;
   if IsKeyDown(VK_F8) then begin
      GLSceneViewer1.Camera:=GLCamera2;
{}      Actor1.Visible:=False;
      Label4.Font.Style:=Label4.Font.Style+[fsBold];
      Label3.Font.Style:=Label3.Font.Style-[fsBold];
   end;

   // Move Actor in the scene

   // if nothing specified, we are standing
   moving:='stand';

   // first, are we running ? if yes give animation & speed a boost
   if IsKeyDown(VK_SHIFT) then begin
      Actor1.Interval:=100;
      boost:=cRunBoost*deltaTime
   end else begin
      Actor1.Interval:=150;
      boost:=deltaTime;
   end;
   Actor2.Interval:=Actor1.Interval;

   // are we advaning/backpedaling ?
   if IsKeyDown(VK_UP) then begin
      GLNavigator1.MoveForward(cWalkStep*boost);
      moving:='run';
   end;
   if IsKeyDown(VK_DOWN) then begin
      GLNavigator1.MoveForward(-cWalkStep*boost);
      moving:='run';
   end;

   // slightly more complex, depending on CTRL key, we either turn or strafe
   if IsKeyDown(VK_LEFT) then begin
      if IsKeyDown(VK_CONTROL) then
          GLNavigator1.StrafeHorizontal(-cStrafeStep*boost)
      else GLNavigator1.TurnHorizontal(-cRotAngle*boost);
      moving:='run';
   end;
   if IsKeyDown(VK_RIGHT) then begin
      if IsKeyDown(VK_CONTROL) then
          GLNavigator1.StrafeHorizontal(cStrafeStep*boost)
      else GLNavigator1.TurnHorizontal(cRotAngle*boost);
      moving:='run';
   end;

   // update animation (if required)
   // you can use faster methods (such as storing the last value of "moving")
   // but this ones shows off the brand new "CurrentAnimation" function :)
   if Actor1.CurrentAnimation<>moving then begin
      Actor1.SwitchToAnimation(moving);
      Actor2.Synchronize(Actor1);
   end;
end;

procedure TATreeForm.GLCadencer1Progress(Sender: TObject; const deltaTime,
  newTime: Double);
begin
   HandleKeys(deltaTime);
   GLUserInterface1.Mouselook;

   GLSceneViewer1.Invalidate;
   GLUserInterface1.MouseUpdate;
end;
                
procedure TATreeForm.Timer1Timer(Sender: TObject);
begin
   Caption:=Format('%.2f FPS', [GLSceneViewer1.FramesPerSecond]);
   GLSceneViewer1.ResetPerformanceMonitor;
end;

procedure TATreeForm.CloseBtnClick(Sender: TObject);
begin
  Close;
end;

procedure TATreeForm.ClearBtnClick(Sender: TObject);
var i:Integer; 
begin
  {Erase the Proxies}
  {DummyCube1.DeleteChildren;}
  for i:=(SpriteTreeCount+MushTreeCount+2)-1 downto 2 do
  begin
  DummyCube1.Children[i].Visible:=False;
  DummyCube1.Children[i].Free;
  end;
  for i:=(TreeCount)-1 downto 0 do
  begin
  GLDummyCube1.Children[i].Visible:=False;
  GLDummyCube1.Children[i].Free;
  end;
  DemoTreePlanted:=False;
  GlsDemoCancelled:=False;
  MushTreeCount:=0;
  SpriteTreeCount:=0;
  TreeCount:=0;
  Tree3dLabel.Caption:=Inttostr(MushTreeCount) + ' 3ds Trees';
  TreeSpriteLabel.Caption:=Inttostr(SpriteTreeCount) + ' Sprite Trees';
  TrealLabel.Caption:=Inttostr(TreeCount) + ' TReal Trees';
     { proxy:=TGLProxyObject(DummyCube1.AddNewChild(TGLProxyObject));}
end;

procedure TATreeForm.ObjectBtnClick(Sender: TObject);
begin
  AddMushrooms(5);
end;
// add a few mushrooms to make the "landscape"
procedure TATreeForm.AddMushrooms(NbMushrooms:Integer);
var
   i : Integer;
   proxy : TGLProxyObject;
   s : TGLVector;
   f : Single;
begin
   // spawn some more mushrooms using proxy objects
   for i:=0 to NbMushrooms-1 do begin
      // create a new proxy and set its MasterObject property
      proxy:=TGLProxyObject(DummyCube1.AddNewChild(TGLProxyObject));
      with proxy do begin
         ProxyOptions:=[pooObjects];
         MasterObject:=FreeForm;
         Visible:=True;
         // retrieve reference attitude
         Direction:=FreeForm.Direction;
         Up:=FreeForm.Up;
         // randomize scale
         s:=FreeForm.Scale.AsVector;  {0.02  0.002}
         f:=(1*Random+1);
         ScaleVector(s, f);
         Scale.AsVector:=s;
         // randomize position
         Position.SetPoint(Random(cSpread)-(cSpread/2),
                           FreeForm.Position.z+{0.8*f}7.8*f,
                           Random(cSpread)-(cSpread/2));
         // randomize orientation
         RollAngle:=Random(360);
         TransformationChanged;
      end;
   end;
   inc(MushTreeCount,NbMushrooms);
   Tree3dLabel.Caption:=Inttostr(MushTreeCount) + ' 3ds Trees';
end;

procedure TATreeForm.SpriteBtnClick(Sender: TObject);
begin
  AddSprites(5);
end;
procedure TATreeForm.AddSprites(Spriter:Integer);
var
  i: Integer;
  s : TGLVector;
  f : Single;
  spr : TGLSprite;
begin

	// New sprites are created by duplicating the template "sprite2"
	for i:=1 to Spriter do begin
		spr:=TGLSprite(DummyCube1.AddNewChild(TGLSprite));
		spr.Assign(GLSprite1);
     with spr do
     begin
         Visible:=True;
         s:=GLSprite1.Scale.AsVector;
         f:=(1*Random+1);
         ScaleVector(s, f);
         Scale.AsVector:=s;
         // randomize position
         Position.SetPoint(Random(cSpread)-(cSpread/2),
                           GLSprite1.Position.z+{0.8*f}15*f,
                           Random(cSpread)-(cSpread/2));
         // sprite size change
{         SetSquareSize(2+cos(3*f)); }
         TransformationChanged;
     end;
	end;
   inc(SpriteTreeCount,Spriter);
   TreeSpriteLabel.Caption:=Inttostr(SpriteTreeCount) + ' Sprite Trees';
end;
{Compression
FilteringQuality
ImageClassName
MagFilter
MappingMode
MinFilter
TextureFormat
TextureMode
TextureWrap}








procedure TATreeForm.TrealTreeBtnClick(Sender: TObject);
var
   proxy : TGLProxyObject;
   s : TGLVector;
   f,r : Single;
begin
  If (not DemoTreePlanted) then
  begin {Open a tree definition file  .tdf}
   OpenTdfDialog.InitialDir:= GlsDefinitionPath;
   if OpenTdfDialog.Execute then
   begin
     Application.ProcessMessages;
     GlsDefinitionPath:=ExtractFilePath(OpenTdfDialog.FileName);
     LoadTreeDefinition(OpenTdfDialog.FileName);
     TrealTreeMaker;
   end else exit;
   inc(TreeCount);
   TrealLabel.Caption:=Inttostr(TreeCount) + ' TReal Trees';
   exit;  {This makes 1 REAL tree and KEEPS it Visible... TOO CPU HARD}
  end;

  // spawn 1 tree using proxy objects
  begin
     r:=Random;
      // create a new proxy and set its MasterObject property
      proxy:=TGLProxyObject(GLDummyCube1.AddNewChild(TGLProxyObject));
      with proxy do begin
         ProxyOptions:=[pooObjects];
         MasterObject:=TreeFreeForm;
         Visible:=True;
         // retrieve reference attitude
         Direction:=TreeFreeForm.Direction;
         Up:=TreeFreeForm.Up;
         // randomize scale
         s:=TreeFreeForm.Scale.AsVector;
         {Change Scale to fit other trees 35..3..1 units}
         f:=(1*Random+1);
         ScaleVector(s, f);
         Scale.AsVector:=s;
         // randomize position
         Position.SetPoint(r*(cSpread)-(cSpread/2),
                           TreeFreeForm.Position.z+0.8*f,
                           r*(cSpread)-(cSpread/2));
         // randomize orientation
         RollAngle:=r*(360);
         TransformationChanged;
      end;
      // create a new proxy and set its MasterObject property
      proxy:=TGLProxyObject(GLDummyCube1.AddNewChild(TGLProxyObject));
      with proxy do begin
         ProxyOptions:=[pooObjects];
         MasterObject:=TreeLeafFF;
         Visible:=True;
         // retrieve reference attitude
         Direction:=TreeFreeForm.Direction;
         Up:=TreeFreeForm.Up;
         // randomize scale
         s:=TreeFreeForm.Scale.AsVector;
         {Change Scale to fit other trees 35..3..1 units}
         f:=(1*Random+1);
         ScaleVector(s, f);
         Scale.AsVector:=s;
         // randomize position
         Position.SetPoint(r*(cSpread)-(cSpread/2),
                           TreeFreeForm.Position.z+0.8*f,
                           r*(cSpread)-(cSpread/2));
         // randomize orientation
         RollAngle:=r*(360);
         TransformationChanged;
      end;
      proxy:=TGLProxyObject(GLDummyCube1.AddNewChild(TGLProxyObject));
      with proxy do begin
         ProxyOptions:=[pooObjects];
         MasterObject:=FruitFF;
         Visible:=True;
         // retrieve reference attitude
         Direction:=TreeFreeForm.Direction;
         Up:=TreeFreeForm.Up;
         // randomize scale
         s:=TreeFreeForm.Scale.AsVector;
         {Change Scale to fit other trees 35..3..1 units}
         f:=(1*Random+1);
         ScaleVector(s, f);
         Scale.AsVector:=s;
         // randomize position
         Position.SetPoint(r*(cSpread)-(cSpread/2),
                           TreeFreeForm.Position.z+0.8*f,
                           r*(cSpread)-(cSpread/2));
         // randomize orientation
         RollAngle:=r*(360);
         TransformationChanged;
      end;
   end;
   inc(TreeCount);
   TrealLabel.Caption:=Inttostr(TreeCount) + ' TReal Trees';
end;

procedure TATreeForm.TrealTreeMaker;
function ExportTreeMesh(AStem: TStem): integer;
  { Pre: AStem <> nil
    Ret: 0, ExportTreeMesh was not successful
         1, ExportTreeMesh was successful, the tree mesh was successfully
            processed by the selected plugin }
  var i: integer;
  begin
    with AStem do
    begin
      Application.ProcessMessages;
      if AddStem(Sections) = 0 then
      begin
        Result := 0;
        Exit;
      end;
      Result := 1;
      for i := 0 to SubStems.Count - 1 do
      begin
        if ExportTreeMesh(TStem(SubStems[i])) = 0 then
        begin
          Result := 0;
          Exit;
        end;
        Result := 1;
      end;
      for i := 0 to Leafs.Count - 1 do
      begin
        if AddLeaf(Leafs[i], FModelParameters.LeafColor, FModelParameters.LeafColorVariation) = 0 then
        begin
          Result := 0;
          Exit;
        end;
        Result := 1;
      end;
      for i := 0 to Fruit.Count - 1 do
      begin
        if AddFruit(Fruit[i], FModelParameters.FruitColor, FModelParameters.FruitColorVariation) = 0 then
        begin
          Result := 0;
          Exit;
        end;
        Result := 1;
      end;
    end;
  end;
Begin
    { Initialize the random number generator }
    if RandomTreeSeedNumber = 0 then Randomize
    else RandSeed := RandomTreeSeedNumber;
    if Random(2) = 0 then WindSign := -1 else WindSign := 1;
    //load the materials
    If FileExists(FModelParameters.TreeBarkFile) then
    GLMaterialLibrary1.AddTextureMaterial('TreeBark', FModelParameters.TreeBarkFile)
    else FModelParameters.TreeBarkFile := '';
    If FileExists(FModelParameters.LeafTextureFile) then
    GLMaterialLibrary1.AddTextureMaterial('LeafTexture', FModelParameters.LeafTextureFile)
    else FModelParameters.LeafTextureFile := '';
    If FileExists(FModelParameters.FruitTextureFile) then
    GLMaterialLibrary1.AddTextureMaterial('FruitTexture', FModelParameters.FruitTextureFile)
    else FModelParameters.FruitTextureFile := '';

   TreeFreeForm.Material.FrontProperties.Emission.AsWinColor:=
     FModelParameters.TreeColor;
   FruitFF.Material.BackProperties.Emission.AsWinColor:=
     FModelParameters.FruitColor;
   TreeLeafFF.Material.BackProperties.Emission.AsWinColor:=
     FModelParameters.LeafBackColor;
  { Create the entire tree structure }
  FTree := TTree.Create(FModelParameters,FModelParameters.Season
  {aGlsTreeForm.SeasonComboBox.ItemIndex});
  FTree.Trunk.CreateStructure(nil, FTree.TrunkLength, 0, 0);
  ProgressBar.Max := FTree.TotalStems + FTree.TotalGrownLeaves + FTree.TotalFruit- 1;
  ProgressBar.Position := 0;

  { And create the tree mesh points }
  FTree.Grow;
  {TreeForm }
  DemoTreePlanted:=True;
  { export the tree structure to a 3d format }
  if not GlsDemoCancelled then
  begin
    { Call the initialization section  }
    if Initialize(FTree.MaxX, FTree.MaxY, FTree.MaxZ, FTree.TotalStems) = 0 then Exit
    else
    begin
      { Export the mesh structure to the FreeForm }
      if ExportTreeMesh(FTree.Trunk) = 0 then
      begin
        MessageDlg('Error computing tree mesh!', mtError, [mbOK], 0);
        Exit;
      end;
    end;
    { Free the memory occupied by the internal representation of the tree
      When the form is Closed  TreePlanted:=True;}

    {Freeform data changed}
    ProgressBar.Position := 0;
    TreeFreeForm.StructureChanged;
    FruitFF.StructureChanged;
    TreeLeafFF.StructureChanged;
  end;
end;

function TATreeForm.Initialize(MaxX, MaxY, MaxZ: real; ATotalStems: integer): integer;
begin
  ProgressBar.Max := ATotalStems + FTree.TotalGrownLeaves+ FTree.TotalFruit- 1;
  { TotalVertices and TotalFaces hold the total amount of vertices and faces
    in the entire tree }
  TotalVertices := 0;
  TotalFaces := 0;
  { TotalStems is used for the internal counting of the total number of stems,
    ATotalStems is used to initialize the progress bar of the progress form }
  TotalStems := 0;
  TotalFruit:= 0;
  TotalFruitVertices:= 0;
  TotalLeaveVertices:= 0;
  TotalDemoLeaves:= 0;
  Bolebusted:=0;
  FormatSettings.DecimalSeparator := '.';
  FormatSettings.ThousandSeparator := #0;
  Result := 1;
end;

{A Stem is a bunch of circles (Sections) formed by points (Vertices)
             stacked as rings to form a 'cylinder'
             that slowly reduces in diameter}
function TATreeForm.AddStem(Sections: TList): integer;
{ Pre: Sections <> nil
  Ret: 0, AddStem failed
       1, AddStem succeeded, a mesh describing the stem segment made
          up by the sections defined in Sections has been written to 'tree.inc'. }
var
  i, j: integer;                           // Loop variables
  Vertices: integer;                // The amount of vertices and
                                    //this was for POV:
                                    // faces respectively in the segment mesh
  VerticesPerSection: integer;             // The amount of vertices in a
                                             // section (is the same for all
                                             // sections)
  TotalSections: integer;                  // Total amount of sections
  Mesh	:TGLMeshObject; // Structure containing the actual data
  FaceGroup: TFGVertexIndexList;
  Sign, Variation, Red, Blue, Green: integer;

begin
  TotalStems := TotalStems + 1;
  { Count the amount of sections }
  TotalSections := Sections.Count;
  { Count the amount of vertices in the stem }
  VerticesPerSection := TList(Sections[0]).Count;
  Vertices := VerticesPerSection * TotalSections;
  { Update the total amount of vertices in the tree }
  TotalVertices := TotalVertices + Vertices;

  Mesh:=TGLMeshObject.CreateOwned(TreeFreeForm.MeshObjects); // Create the mesh
  Mesh.Mode := momFaceGroups;
    { Write vertices making up the mesh }
    { For every section making up the stem write the vertices from that section
      to the vertex list of the mesh object }
  for i := 0 to TotalSections - 1 do
  begin
    for j := 0 to VerticesPerSection - 1 do
    begin
      { Repeatedly write a vertex from the current section }
      Mesh.Vertices.add(TPoint3d(TList(Sections[i])[j]).x,
          TPoint3d(TList(Sections[i])[j]).y,
          TPoint3d(TList(Sections[i])[j]).z);
      Mesh.Normals.Add(TPoint3d(TList(Sections[i])[j]).x,
          TPoint3d(TList(Sections[i])[j]).y,
          TPoint3d(TList(Sections[i])[j]).z);
      If FModelParameters.TreeBarkFile = '' then
      begin
        {Mesh.Colors.Add(0,0,1,0);}
        { Calculate the red, green and blue components from the leafcolor variable }
        Red := FModelParameters.TreeColor and $FF;
        Green := (FModelParameters.TreeColor and $FF00) shr 8;
        Blue := (FModelParameters.TreeColor and $FF0000) shr 16;

        if FModelParameters.TreeColorVariation then
        begin
          { Add a small variation to the leaf color }
          if Random(2) = 0 then Sign := 1 else Sign := -1;
          Variation := Random(40);
          Red := Red + Sign * Variation;
          Green := Green + Sign * Variation;
          Blue := Blue + Sign * Variation;

          if Red < 0 then Red := 0;
          if Green < 0 then Green := 0;
          if Blue < 0 then Blue := 0;
        end;
        Mesh.Colors.Add((Red / $FF),(Green / $FF),(Blue / $FF),0);
      end;
        //set the tex coords to cover the whole mesh
      with Mesh.TexCoords do   {This tiles the texture}
      begin       {horizonatl s, vertical t}
     {Add( (j/(VerticesPerSection-1)), i);}
      If Bolebusted > 8 then
        Add( (j/(VerticesPerSection-1)), i)
        else
        If Bolebusted <7 then
        Add( (j/(VerticesPerSection-1)), (i/6))
        else
        Add( (j/(VerticesPerSection-1)),  (i/9))
      end;
    end;
  end;

        //add a new facegroup
  facegroup := TFGVertexIndexList.CreateOwned(Mesh.FaceGroups);
     //and set the material
  If FModelParameters.TreeBarkFile <> '' then
     facegroup.MaterialName := 'TreeBark';
    {Write the face indices }
    { For every section write the segment mesh. The outer loop has a maximum of
      TotalSections - 2 because the faces are built using points from section i
      and section i + 1}
  for i := 0 to TotalSections - 2 do
  begin
    for j := 0 to VerticesPerSection - 1 do
    begin
        { Repeatedly write two face indices creating a cubic little plane between
          four vertices   :
          VerticesPerSection * i + j: lower right corner of the first face
          VerticesPerSection * (i + 1) + j: upper right corner of the first face
          VerticesPerSection * i + (j + 1) mod VerticesPerSection: lower left corner of the first face
          VerticesPerSection * i + (j + 1) mod VerticesPerSection: lower left corner of the second face
          VerticesPerSection * (i + 1) + j: upper right corner of the second face
          VerticesPerSection * (i + 1) + (j + 1) mod VerticesPerSection: upper left corner of the second face }
      with facegroup do
      begin
        add((VerticesPerSection * i + j));
        add((VerticesPerSection * (i + 1) + j));
        add((VerticesPerSection * i + (j + 1) mod VerticesPerSection));
        add((VerticesPerSection * i + (j + 1) mod VerticesPerSection));
        add((VerticesPerSection * (i + 1) + j));
        add((VerticesPerSection * (i + 1) + (j + 1) mod VerticesPerSection));
      end;
    end;
  end;
    {These facegroups also have a range of rendering modes:
      fgmmTriangles, fgmmTriangleStrip, fgmmFlatTriangles and fgmmTriangleFan.}
  FaceGroup.Mode := fgmmTriangles;
  ProgressBar.StepIt;
  Result := 1;
end;


{Each Leaf is a closed Polygon...
 use Mesh to place ALL leaves into the Freeform
 The Real LOD advantage would be to make 1 Leaf and Proxies the rest...}
function TATreeForm.AddLeaf(Points: TList; LeafColor: integer; LeafColorVariation: boolean): integer;
var  i: integer;
    Sign: integer;
    Red, Blue, Green: integer;
    Variation: integer;
  Mesh	:TGLMeshObject; // Structure containing the actual data
  FaceGroup: TFGVertexIndexList;
begin
  TotalDemoLeaves := TotalDemoLeaves + 1;
  If LeafOffCB.Checked then begin end else
  begin
                               {TreeFreeForm TreeLeafFF}
    Mesh:=TGLMeshObject.CreateOwned(TreeLeafFF.MeshObjects); // Create the mesh
    Mesh.Mode := momFaceGroups; {(momTriangles, momTriangleStrip, momFaceGroups);}
{    TotalLeaveVertices:=TotalLeaveVertices+Points.Count;}
    for i := 0 to Points.Count - 1 do
    begin
      Mesh.Vertices.add (TPoint3d(Points[i]).x,
                         TPoint3d(Points[i]).y,
                         TPoint3d(Points[i]).z);
      Mesh.Normals.Add(TPoint3d(Points[i]).x,
                       TPoint3d(Points[i]).y,
                       TPoint3d(Points[i]).z);
      If FModelParameters.LeafTextureFile = '' then
      begin
        { Calculate the red, green and blue components from the leafcolor variable }
        {This is broke.. also need to make Front and Bacl Leaf colors}
        Red := LeafColor and $FF;
        Green := (LeafColor and $FF00) shr 8;
        Blue := (LeafColor and $FF0000) shr 16;
        if LeafColorVariation then
        begin
          { Add a small variation to the leaf color }
          if Random(2) = 0 then Sign := 1 else Sign := -1;
          Variation := Random(40);
          Red := Red + Sign * Variation;
          Green := Green + Sign * Variation;
          Blue := Blue + Sign * Variation;
          if Red < 0 then Red := 0;
          if Green < 0 then Green := 0;
          if Blue < 0 then Blue := 0;
        end;
        Mesh.Colors.Add((Red / $FF),(Green / $FF),(Blue / $FF),0);
      end;
      {LeafTexture}
       //set the tex coords to cover the whole mesh
      with Mesh.TexCoords do   {This does Not tile the texture,corrects offset}
      begin       {horizonatl s, vertical t}
        Add( LeafPolygonVertices[FModelParameters.LeafShape,i,0]+0.5,
              LeafPolygonVertices[FModelParameters.LeafShape,i,1]);
      end;
    end;
    //add a new facegroup
    facegroup := TFGVertexIndexList.CreateOwned(Mesh.FaceGroups);
      //and set the material
    If FModelParameters.LeafTextureFile <> '' then
        facegroup.MaterialName := 'LeafTexture';
        {Leaves are clockwise Triangle fans: make triangles}
    for i := 1 to Points.Count - 3 do
    with facegroup do
    begin
      add(0); add(i ); add(i+1);
      add(0); add(i + 1); add(i+2);
    end;
      {These facegroups also have a range of rendering modes:
      fgmmTriangles, fgmmTriangleStrip, fgmmFlatTriangles and fgmmTriangleFan.}
    FaceGroup.Mode := fgmmTriangleFan;   {Winding clockwise (reversed) glFrontFace}
  end;
  ProgressBar.StepIt;
  Result := 1;
end;

function TATreeForm.AddFruit(Points: TList; FruitColor: integer; FruitColorVariation: boolean): integer;
var  i: integer;
    Sign: integer;
    Red, Blue, Green: integer;
    Variation: integer;
  Mesh	:TGLMeshObject; // Structure containing the actual data
  FaceGroup: TFGVertexIndexList;
begin
  TotalFruit := TotalFruit + 1;
  If DisplayWithLeaves then
  begin
                               {LeafFF FruitFF}
    Mesh:=TGLMeshObject.CreateOwned(FruitFF.MeshObjects); // Create the mesh
    Mesh.Mode := momFaceGroups; {(momTriangles, momTriangleStrip, momFaceGroups);}
    Mesh.Vertices.Clear;
    TotalFruitVertices:=TotalFruitVertices+Points.Count;
    for i := 0 to Points.Count - 1 do
    begin
      Mesh.Vertices.add (TPoint3d(Points[i]).x,
                         TPoint3d(Points[i]).y,
                         TPoint3d(Points[i]).z);
      Mesh.Normals.Add(TPoint3d(Points[i]).x,
                       TPoint3d(Points[i]).y,
                       TPoint3d(Points[i]).z);
      If FModelParameters.FruitTextureFile = '' then
      begin
        { Calculate the red, green and blue components from the leafcolor variable }
        {This is broke.. also need to make Front and Bacl Leaf colors}
        Red := FruitColor and $FF;
        Green := (FruitColor and $FF00) shr 8;
        Blue := (FruitColor and $FF0000) shr 16;
        if FruitColorVariation then
        begin
          { Add a small variation to the leaf color }
          if Random(2) = 0 then Sign := 1 else Sign := -1;
          Variation := Random(40);
          Red := Red + Sign * Variation;
          Green := Green + Sign * Variation;
          Blue := Blue + Sign * Variation;
          if Red < 0 then Red := 0;
          if Green < 0 then Green := 0;
          if Blue < 0 then Blue := 0;
        end;
        Mesh.Colors.Add((Red / $FF),(Green / $FF),(Blue / $FF),0);
      end;
      {LeafTexture}
       //set the tex coords to cover the whole mesh
      with Mesh.TexCoords do   {This does Not tile the texture,corrects offset}
      begin       {horizonatl s, vertical t}
        Add( FruitPolygonVertices[i,0]+0.5,
              FruitPolygonVertices[i,1]);
      end;
    end;
    //add a new facegroup
    facegroup := TFGVertexIndexList.CreateOwned(Mesh.FaceGroups);
      //and set the material
    If FModelParameters.FruitTextureFile <> '' then
        facegroup.MaterialName := 'FruitTexture';
        {Fruit are clockwise Triangle fans: make triangles}
    for i := 1 to Points.Count - 3 do
    with facegroup do
    begin
      add(0); add(i ); add(i+1);
      add(0); add(i + 1); add(i+2);
    end;
      {These facegroups also have a range of rendering modes:
      fgmmTriangles, fgmmTriangleStrip, fgmmFlatTriangles and fgmmTriangleFan.}
    FaceGroup.Mode := fgmmTriangleFan;   {Winding clockwise (reversed) glFrontFace}
  end;
  ProgressBar.StepIt;
  Result := 1;
end;


end.
